package com.mygdx.game;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;

import java.util.Random;

import static com.badlogic.gdx.graphics.Color.*;
import static java.lang.Math.*;

public class MyGdxGame extends ApplicationAdapter {
    SpriteBatch batch;
    Texture img;
    Pixmap map;
    int width;
    int height;
    Random r;
    OrthographicCamera camera;
    int black = BLACK.toIntBits();
    int blank = WHITE.toIntBits();
    int gray = GRAY.toIntBits();

    double oct = 100d;
    double section = 1d / oct;
    int iterations = 1; // 分形迭代次数

    int offsetX;
    int offsetY;

    NoiseType noiseType = NoiseType.PerlinNoise;

    enum NoiseType {
        PerlinNoise, SimpleNoise
    }

    class MyInputProcess extends InputAdapter {
        @Override
        public boolean keyDown(int keycode) {
            Gdx.app.log("keys", String.valueOf(keycode));
            return false;
        }
    }

    private double gradient(double x) {
        double period = PI / 2d;
        return -sin(x * period);
    }


    @Override
    public void create() {
//        Gdx.input.setInputProcessor(new MyInputProcess());
        width = Gdx.graphics.getWidth();
        height = Gdx.graphics.getHeight();
        camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        r = new Random();
        offsetX = r.nextInt(10000);
        offsetY = r.nextInt(10000);
        batch = new SpriteBatch();
        map = new Pixmap(width, height, Pixmap.Format.RGB888);
        img = new Texture(map);
        updatePixmap();
        camera.position.set(Gdx.graphics.getWidth() >> 1, Gdx.graphics.getHeight() >> 1, 0);
    }

    @Override
    public void render() {
        processInput();
        camera.update();
        batch.setProjectionMatrix(camera.combined);
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        batch.begin();
        batch.draw(img, 0, 0);
        batch.end();
    }

    private double fractal(int x, int y, int iterations) {
        double sum = 0;
        for (int i = 0; i < iterations; i++) {
            sum += octave(x, y, i + 1);
        }
        return sum;
    }

    private double octave(int x, int y, int d) {
        double t = pow(2, d);
        double rst = 0d;
        switch (noiseType) {
            case PerlinNoise:
                rst = PerlinNoise.noise(t * (x + offsetX) / oct, t * (y + offsetY) / oct) / t;
                break;
            case SimpleNoise:
                rst = SimplexNoise.noise(t * (x + offsetX) / oct, t * (y + offsetY) / oct) / t;
        }
        return rst;
    }

    private void updatePixmap() {
        map.setColor(blank);
        map.fill();
        double p = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                p = fractal(x, y, iterations);
                map.drawPixel(x, y, p > gradient((double) y * 2d / (double) height - 1d) ? gray : blank);
            }
        }
        Gdx.app.log("sections and p", String.format("%s %s", section, p));
        img.draw(map, 0, 0);
    }

    private void processInput() {
        if (Gdx.input.isKeyPressed(Input.Keys.LEFT))
            camera.position.x -= 2;
        if (Gdx.input.isKeyPressed(Input.Keys.RIGHT))
            camera.position.x += 2;
        if (Gdx.input.isKeyPressed(Input.Keys.UP))
            camera.position.y += 2;
        if (Gdx.input.isKeyPressed(Input.Keys.DOWN))
            camera.position.y -= 2;
        if (Gdx.input.isKeyPressed(Input.Keys.PAGE_UP))
            camera.zoom = (float) MathUtils.clamp(camera.zoom + 0.5, 0, 10f);
        if (Gdx.input.isKeyPressed(Input.Keys.PAGE_DOWN))
            camera.zoom = (float) MathUtils.clamp(camera.zoom - 0.5, 1, 10f);
        if (Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE))
            Gdx.app.exit();
        if (Gdx.input.isKeyPressed(Input.Keys.NUM_1)) {
            section -= 1 / (oct * 10);
            Gdx.app.log("-", String.valueOf(section));
            updatePixmap();
        }
        if (Gdx.input.isKeyPressed(Input.Keys.NUM_2)) {
            section += 1 / (oct * 10);
            Gdx.app.log("+", String.valueOf(section));
            updatePixmap();
        }
        if (Gdx.input.isKeyJustPressed(Input.Keys.F1)) {
            oct *= 10d;
            section = 1d / oct;
            updatePixmap();
        }
        if (Gdx.input.isKeyJustPressed(Input.Keys.F2)) {
            oct /= 10d;
            section = 1d / oct;
            updatePixmap();
        }

        if (Gdx.input.isKeyJustPressed(Input.Keys.ENTER)) {
            offsetX = r.nextInt(10000);
            offsetY = r.nextInt(10000);
            Gdx.app.log("ramdon", String.format("offsetX:%s,offsetY:%s", offsetX, offsetY));
            updatePixmap();
        }

        if (Gdx.input.isKeyJustPressed(Input.Keys.EQUALS)) {
            iterations += 1;
            Gdx.app.log("fractal iterations", String.valueOf(iterations));
            updatePixmap();
        }
        if (Gdx.input.isKeyJustPressed(Input.Keys.MINUS)) {
            iterations -= 1;
            Gdx.app.log("fractal iterations", String.valueOf(iterations));
            updatePixmap();
        }

        if (Gdx.input.isKeyPressed(Input.Keys.F5)) {
            noiseType = NoiseType.PerlinNoise;
            Gdx.app.log("Noise type", String.valueOf(noiseType));
            updatePixmap();
        }

        if (Gdx.input.isKeyPressed(Input.Keys.F6)) {
            noiseType = NoiseType.SimpleNoise;
            Gdx.app.log("Noise type", String.valueOf(noiseType));
            updatePixmap();
        }

    }

    @Override
    public void dispose() {
        batch.dispose();
        map.dispose();
        img.dispose();
    }
}

